
module mo_synoz

  !--------------------------------------------------------------------
  !	... synthetic stratospheric ozone emission source
  !--------------------------------------------------------------------

  use shr_kind_mod, only : r8 => shr_kind_r8
  use cam_logfile,  only : iulog

  implicit none

  save 

  real(r8), allocatable :: po3(:,:,:)

  private
  public :: synoz_inti
  public :: po3

contains

  subroutine synoz_inti( )
    !-----------------------------------------------------------------------
    ! 	... initialize synoz emissions
    !	    note: the emissions are in in units of molecules/cm**3/s
    !-----------------------------------------------------------------------

    use dyn_grid,         only : get_dyn_grid_parm
    use ppgrid,           only : pcols, begchunk, endchunk, pver, pverp
    use ref_pres,         only : pref_mid, pref_edge
    use phys_grid,        only : scatter_field_to_chunk
    use chem_mods,        only : adv_mass
    use mo_chem_utls,     only : get_spc_ndx
    use spmd_utils,       only : masterproc
    use physconst,        only : pi, &
                                 grav => gravit, & ! m/s^2
                                 dry_mass => mwdry, & ! kg/kmole
                                 seconds_in_day => cday, &
                                 rearth  ! m
    use cam_abortutils,   only : endrun
    use dyn_grid,         only : get_dyn_grid_parm_real1d

    implicit none

    !-----------------------------------------------------------------------
    ! 	... dummy arguments
    !-----------------------------------------------------------------------

    !-----------------------------------------------------------------------
    !	... local variables
    !-----------------------------------------------------------------------
    real(r8), parameter :: latmin                = -30._r8
    real(r8), parameter :: latmax                = 30._r8
    real(r8), parameter :: prsmin                = 1000._r8
    real(r8), parameter :: prsmax                = 7000._r8
    real(r8), parameter :: ozone_source_per_year = 500._r8     ! global/annual average of ozone source (Tg/yr)
    real(r8), parameter :: tg2kg                 = 1.e9_r8
    real(r8), parameter :: days_per_year         = 365._r8

    integer :: i, j, k, jl, ju
    integer :: jmin, jmax, kmin, kmax
    integer :: synoz_ndx
    integer :: astat
    integer :: plon, plat, plev

    real(r8)    :: diff, diff_min, diff_max
    real(r8)    :: total_mass = 0._r8
    real(r8)    :: seq
    real(r8), allocatable    :: sf(:)
    real(r8), allocatable :: prs(:)
    real(r8), allocatable :: dp(:)
    real(r8), allocatable :: wk(:,:,:)
    real(r8), allocatable :: lat(:),latwts(:)

    allocate( po3(pcols,pver,begchunk:endchunk), stat=astat )
    if( astat /= 0 ) then
       write(iulog,*) 'synoz_inti: failed to allocate po3; error = ',astat
       call endrun
    end if

    plon = get_dyn_grid_parm('plon')
    plat = get_dyn_grid_parm('plat')
    plev = get_dyn_grid_parm('plev')

    allocate(lat(plat),latwts(plat) )

    lat = get_dyn_grid_parm_real1d('latdeg')
    latwts = get_dyn_grid_parm_real1d('w')

    allocate( wk(plon,plev,plat), stat=astat )
    if( astat /= 0 ) then
       write(iulog,*) 'synoz_inti: failed to allocate wk; error = ',astat
       call endrun
    end if

    Masterproc_only :  if( masterproc ) then
       !-----------------------------------------------------------------------
       ! 	... allocate memory -- for global grid
       !-----------------------------------------------------------------------
       allocate(  prs(plev), dp(plev), stat=astat )
       if( astat /= 0 ) then
          write(iulog,*) 'synoz_inti: failed to allocate prs, dp; error = ',astat
          call endrun
       end if

       !-----------------------------------------------------------------------
       ! 	... find indices of the latitudinal box
       !-----------------------------------------------------------------------
       jmin = -99
       jmax = -99
       diff_min = 1.e20_r8
       diff_max = 1.e20_r8
       do j = 1,plat
          diff = abs(lat(j)-latmin)
          if( diff < diff_min ) then
             diff_min = diff
             jmin = j
          end if
          diff = abs(lat(j)-latmax)
          if( diff < diff_max ) then
             diff_max = diff
             jmax = j
          end if
       end do

       !-----------------------------------------------------------------------
       ! 	... make sure we found them
       !-----------------------------------------------------------------------
       if( jmin < 0 .or. jmax < 0 ) then
          write(iulog,*) 'synoz_inti: problem finding the min/max lat in synoz_inti',jmin,jmax
          call endrun
       end if
       !-----------------------------------------------------------------------
       ! 	... define pressure arrays, assuming surface pressure = 1000 hPa
       !-----------------------------------------------------------------------
       prs(:) = pref_mid(:)
       dp(:)  = pref_edge(2:pverp) - pref_edge(1:pver)

       !-----------------------------------------------------------------------
       ! 	... find indices of the pressure box
       !-----------------------------------------------------------------------
       kmin = -99
       kmax = -99
       diff_min = 1.e20_r8
       diff_max = 1.e20_r8
       do k = 1,plev
          diff = abs( prs(k) - prsmin )
          if( diff < diff_min ) then
             diff_min = diff
             kmin = k
          end if
          diff = abs( prs(k) - prsmax )
          if( diff < diff_max ) then
             diff_max = diff
             kmax = k
          end if
       end do

       !-----------------------------------------------------------------------
       ! 	... make sure we found them
       !-----------------------------------------------------------------------
       if( kmin < 0 .or. kmax < 0 ) then
          write(iulog,*) 'synoz_inti: problem finding the min/max prs in synoz_inti',kmin,kmax
          call endrun
       end if

       !-----------------------------------------------------------------------
       ! 	... define geometric factors (in SI)
       !-----------------------------------------------------------------------
       seq = 2._r8*pi*rearth**2/real(plon)
       allocate(sf(plat))
       do j = 1,plat
          sf(j) = seq*latwts(j)
       end do

       !-----------------------------------------------------------------------
       ! 	... find index of synoz
       !-----------------------------------------------------------------------
       synoz_ndx = get_spc_ndx('SYNOZ')
       has_synoz : if( synoz_ndx > 0 ) then
          !-----------------------------------------------------------------------
          ! 	... compute total mass (in kg) over the domain for which
          !           the ozone source will be defined
          !-----------------------------------------------------------------------
          total_mass = 0._r8
          do k = kmin,kmax
             do j = jmin,jmax
                total_mass = total_mass + dp(k)/grav * sf(j)
             end do
          end do
          total_mass = total_mass * plon
#ifdef DEBUG
          write(iulog,*)'synoz_inti: total mass = ',total_mass
#endif
          !-----------------------------------------------------------------------
          ! 	... define the location of the ozone source
          !-----------------------------------------------------------------------
          wk(:,:,:) = 0._r8
          do k = kmin,kmax
             do j = jmin,jmax
                wk(1:plon,k,j) = 1._r8
             end do
          end do
          !-----------------------------------------------------------------------
          ! 	... define the ozone source as vmr/s (what is needed for the chemistry solver)
          !           note : a change in chemdr is made to avoid the division by invariants
          !-----------------------------------------------------------------------
          wk(:,:,:) = wk(:,:,:) * (ozone_source_per_year*tg2kg/total_mass) &
                                / (seconds_in_day*days_per_year) &
                                * (dry_mass/adv_mass(synoz_ndx))
          write(iulog,*) 'synoz_inti: max wk = ',maxval( wk(:,:,:) )
          deallocate( prs, dp )
       end if has_synoz

       deallocate(sf)

    endif Masterproc_only

    !-----------------------------------------------------------------------
    ! 	... scatter to mpi tasks
    !-----------------------------------------------------------------------
    call scatter_field_to_chunk(1, plev, 1, plon, wk, po3)

    !-----------------------------------------------------------------------
    ! 	... deallocate memory
    !-----------------------------------------------------------------------
    deallocate( wk )
    deallocate( lat, latwts )

  end subroutine synoz_inti

end module mo_synoz
